//standard
#include <istream>
#include <numeric>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>

// boost
#include <boost/format.hpp>

// components
#include <tree.h>
#include <index_string.h>
#include <ultoa.h>
#include <progress_indicator.h>
#include <print_error.h>
#include <portable_timer.h>
#include <perlutil.h>
#include <bcpp_parsefasta.h>
#include <stlhelper.h>
#define DEBUG_LEVEL 6
#include <debug_func.h>
#include <tree_extract_sub_tree.h>
#include <tree_ops.h>

#include <boost/tuple/tuple.hpp>

#include "get_arg.h"

using boost::format;
using std::vector;
using std::ostringstream;

using std::string;
using std::fill;
using std::ofstream;
using std::cout;
using std::cin;
using std::cerr;
using std::ifstream;
using std::pair;
using std::istream;
using std::max;
using std::min;
using namespace bcpp_tree_ops;
using namespace bcpp_tree;
using boost::tie;

void report_branches_extracted(const op_extract_branches& sub_trees, ostream& VERBOSE)
{
	// how many subtrees extracted?
	VERBOSE << "\t" << sub_trees.node_UIDs.size() << " branches extracted.\n";

	if (sub_trees.cnt_tree_extracted_by_len)
		VERBOSE << "\t" << sub_trees.cnt_tree_extracted_by_len
					<< " branches with lengths above threshold.\n";
	if (sub_trees.cnt_tree_extracted_by_size)
		VERBOSE << "\t" << sub_trees.cnt_tree_extracted_by_size
					<< " branches with number of leaves above threshold.\n";
}

bool read_tree(bcpp_tree::t_tree& tree, unsigned& cnt_lines)
{
   unsigned tree_line_pos = cnt_lines;
   cin >> t_tree_io_wrapper(tree, cnt_lines);
   if (!cin)
	   return false;
   if (!tree.get_name().length())
	   throw std::runtime_error("Tree on line " + ultoa(tree_line_pos) +
								   " has no name");
   return true;
}


//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

//	main

//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
int main (int argc, char *argv[])
{
	debug_func_cerr(5);

	// we use cout to print progress so flush per call
	//cout.setf(std::ios_base::unitbuf);

	std::ios::sync_with_stdio(false);

	t_program_args args;
	try
	{
		if (!process_command_line_options(argc, argv, args))
			return 1;


	//    char buff[4096];
	//    cin.rdbuf()->pubsetbuf(buff, sizeof buff ); // enable buffering


		t_progress_indicator dots(args.VERBOSE_ONLY(1),  1000);
		unsigned cnt_trees = 0;
		unsigned cnt_lines = 0;
		args.VERBOSE(1) << "\tExtracting large tree branches...\n";
		unsigned sub_tree_cnt = 0;
		while (cin)
		{
			//
			//	read tree from cin
			//
			args.VERBOSE(2) << "\n\tReading tree...\n";
			bcpp_tree::t_tree tree;
			if (!read_tree(tree, cnt_lines))
			{
				args.VERBOSE(2) << "\tNo more trees.\n\n";
				break;
			}


			++cnt_trees;

			//
			//	extract heads of branches
			//
			args.VERBOSE(2) << "\tLook for large tree branches above thresholds...\n";
			op_extract_branches sub_trees(	tree, args.node_limit, args.len_limit);
			if (!sub_trees.node_UIDs.size())
				continue;
			report_branches_extracted(sub_trees,  args.VERBOSE(2));



			//
			//	write denuded tree in nh format and extract sub-trees corresponding to
			//			truncated branches
			//
			//		add offset when referring to sub-branches so that different trees
			//			on the same file have unique branch names
			std::ostringstream ignore;
			op_nh_strm_post_branch_extract main_tree_after_extract(	ignore,
																	sub_trees.node_UIDs,
																	sub_tree_cnt);
			main_tree_after_extract(tree.get_root());

			//
			//	Extracting branches
			//
			args.VERBOSE(2) << "\tExtracting branches...\n";
			for (unsigned i = 0; i < main_tree_after_extract.cnt_sub_trees; ++i)
			{
					cout
						<< ">" << main_tree_after_extract.sub_tree_name(i) <<  '\n'
						<< *main_tree_after_extract.nodes[i]
						<< "\n";
			}
			sub_tree_cnt += main_tree_after_extract.cnt_sub_trees;

		}
		if (!cnt_trees)
			throw std::logic_error("No tree in New Hampshire format was found.");
	}
	catch(std::exception& e)
	{
		std_print_error(args.CERR(), e.what());
		return 1;
	}

	args.VERBOSE(1) << "\tAll finished!!\n\n";

	return 0;
}



